package generator

import (
	"database/sql"
	"fmt"
	"strings"

	"generator-tool/config"
	"generator-tool/internal/model"

	_ "github.com/go-sql-driver/mysql"
)

type DBInspector struct {
	db *sql.DB
}

func NewDBInspector(cfg *config.GeneratorConfig) (*DBInspector, error) {
	dsn := cfg.GetDSN()
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("连接数据库失败: %v", err)
	}

	// 测试连接
	if err := db.Ping(); err != nil {
		return nil, fmt.Errorf("数据库连接测试失败: %v", err)
	}

	return &DBInspector{db: db}, nil
}

func (i *DBInspector) GetTableInfo(tableName string) (*model.TableInfo, error) {
	// 查询表结构
	query := `
        SELECT 
            column_name,
            data_type,
            column_type,
            is_nullable,
            column_key,
            column_default,
            extra,
            column_comment
        FROM information_schema.columns 
        WHERE table_schema = DATABASE() AND table_name = ?
        ORDER BY ordinal_position
    `

	rows, err := i.db.Query(query, tableName)
	if err != nil {
		return nil, fmt.Errorf("查询表结构失败: %v", err)
	}
	defer rows.Close()

	var columns []model.ColumnInfo
	for rows.Next() {
		var col model.ColumnInfo
		err := rows.Scan(
			&col.ColumnName,
			&col.DataType,
			&col.ColumnType,
			&col.IsNullable,
			&col.ColumnKey,
			&col.ColumnDefault,
			&col.Extra,
			&col.ColumnComment,
		)
		if err != nil {
			return nil, fmt.Errorf("扫描列信息失败: %v", err)
		}
		columns = append(columns, col)
	}

	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("遍历结果失败: %v", err)
	}

	return &model.TableInfo{
		TableName: tableName,
		Columns:   columns,
	}, nil
}

func (i *DBInspector) ConvertToFields(columns []model.ColumnInfo) []model.FieldInfo {
	var fields []model.FieldInfo

	for _, col := range columns {
		field := model.FieldInfo{
			Name:     i.toCamelCase(col.ColumnName),
			Type:     i.getGoType(col.DataType, col.IsNullable),
			Json:     col.ColumnName,
			Comment:  col.ColumnComment,
			IsPK:     strings.ToUpper(col.ColumnKey) == "PRI",
			Nullable: strings.ToUpper(col.IsNullable) == "YES",
		}

		// 构建GORM tag
		var tags []string
		if field.IsPK {
			tags = append(tags, "primaryKey")
		}

		// 添加列名
		if col.ColumnName != strings.ToLower(field.Name) {
			tags = append(tags, fmt.Sprintf("column:%s", col.ColumnName))
		}

		// 添加数据类型
		if col.ColumnType != "" {
			tags = append(tags, fmt.Sprintf("type:%s", col.ColumnType))
		}

		if !field.Nullable {
			tags = append(tags, "not null")
		}

		if strings.ToUpper(col.ColumnKey) == "UNI" {
			tags = append(tags, "uniqueIndex")
		}

		// 正确处理ColumnDefault为sql.NullString的情况
		if col.ColumnDefault.Valid && col.ColumnDefault.String != "NULL" {
			tags = append(tags, fmt.Sprintf("default:%s", col.ColumnDefault.String))
		}

		field.Tag = "gorm:\"" + strings.Join(tags, ";") + "\""

		fields = append(fields, field)
	}

	return fields
}

func (i *DBInspector) getGoType(sqlType, nullable string) string {
	typeMap := map[string]string{
		"int":        "int",
		"integer":    "int",
		"bigint":     "int64",
		"smallint":   "int16",
		"tinyint":    "int8",
		"mediumint":  "int32",
		"varchar":    "string",
		"char":       "string",
		"text":       "string",
		"mediumtext": "string",
		"longtext":   "string",
		"datetime":   "time.Time",
		"timestamp":  "time.Time",
		"date":       "time.Time",
		"time":       "string",
		"decimal":    "float64", // 金额建议使用 string 避免精度问题
		"numeric":    "float64", // 金额建议使用 string 避免精度问题
		"float":      "float32",
		"double":     "float64",
		"bit":        "[]byte",
		"blob":       "[]byte",
		"binary":     "[]byte",
		"varbinary":  "[]byte",
		"boolean":    "bool",
		"bool":       "bool",
		"enum":       "string",
		"set":        "string",
	}

	// 处理可空类型
	isNullable := strings.ToUpper(nullable) == "YES"

	baseType := "interface{}"
	if t, ok := typeMap[strings.ToLower(sqlType)]; ok {
		baseType = t
	}

	// 如果是可空且不是指针类型，考虑使用 sql.Null* 类型
	if isNullable && baseType != "interface{}" {
		// 这里可以根据需要决定是否使用指针或者 sql.Null* 类型
		// 简化处理：对于基本类型使用指针
		switch baseType {
		case "int":
			return "*int"
		case "int64":
			return "*int64"
		case "int32":
			return "*int32"
		case "int16":
			return "*int16"
		case "int8":
			return "*int8"
		case "string":
			return "*string"
		case "bool":
			return "*bool"
		case "float32":
			return "*float32"
		case "float64":
			return "*float64"
		case "time.Time":
			return "*time.Time"
		default:
			return "*" + baseType
		}
	}

	return baseType
}

func (i *DBInspector) toCamelCase(snake string) string {
	parts := strings.Split(snake, "_")
	for i := 0; i < len(parts); i++ { // 修改为从索引0开始
		if len(parts[i]) > 0 {
			parts[i] = strings.ToUpper(parts[i][:1]) + parts[i][1:]
		}
	}
	return strings.Join(parts, "")
}

// GetAllTables 获取数据库中所有表名
func (i *DBInspector) GetAllTables() ([]string, error) {
	query := `
        SELECT table_name 
        FROM information_schema.tables 
        WHERE table_schema = DATABASE() 
        AND table_type = 'BASE TABLE'
        ORDER BY table_name
    `

	rows, err := i.db.Query(query)
	if err != nil {
		return nil, fmt.Errorf("查询表列表失败: %v", err)
	}
	defer rows.Close()

	var tables []string
	for rows.Next() {
		var tableName string
		if err := rows.Scan(&tableName); err != nil {
			return nil, fmt.Errorf("扫描表名失败: %v", err)
		}
		tables = append(tables, tableName)
	}

	if err = rows.Err(); err != nil {
		return nil, fmt.Errorf("遍历结果失败: %v", err)
	}

	return tables, nil
}

func (i *DBInspector) Close() {
	if i.db != nil {
		i.db.Close()
	}
}
